#include "GLTexture2D.h"
#include "corona.h"
#include "Logger.h"
#include "Exception.h"

using namespace game_engine::open_gl;
using namespace game_engine::utils;

///////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
///////////////////////////////////////////////////////////////////////////////

GLTexture2D::GLTexture2D(void) 
    : name(0), fileName(), data(NULL), contentSize(0.0f, 0.0f), pixelsWide(0), 
    pixelsHigh(0), pixelFormat(kGLTexture2DPixelFormat_Automatic), maxS(1.0f), 
    maxT(1.0f)
{
}

GLTexture2D::GLTexture2D(void *d, const GLTexture2DPixelFormat pf, 
                         const UINT32 pw, const UINT32 ph, const Size &s) 
    : name(0), fileName(), data(d), contentSize(s), pixelsWide(pw), 
    pixelsHigh(ph), pixelFormat(pf), maxS(1.0f), maxT(1.0f)
{
}

GLTexture2D::GLTexture2D(const string &file, const INT32 type)
    : name(0), fileName(file), data(NULL), contentSize(0.0f, 0.0f), 
    pixelsWide(0), pixelsHigh(0), maxS(1.0f), maxT(1.0f)
{   
	if (type == GL_RGB)
	{
        pixelFormat = kGLTexture2DPixelFormat_RGB565;
	}
	else if (type == GL_RGBA)
	{
        pixelFormat = kGLTexture2DPixelFormat_RGBA8888;
	}
	else 
    {
        // throw exception
    }
}

GLTexture2D::~GLTexture2D(void)
{
}

///////////////////////////////////////////////////////////////////////////////
// Other public methods
///////////////////////////////////////////////////////////////////////////////

void GLTexture2D::drawAtPoint(const Point &point)
{
	GLfloat	width = static_cast<GLfloat>(getWidth() * maxS);
	GLfloat height = static_cast<GLfloat>(getHeight() * maxT);

    glBindTexture(GL_TEXTURE_2D, name);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, maxT);	glVertex2f(-width / 2.0f + point.getX(),	-height / 2.0f + point.getY());
		glTexCoord2f(maxS, maxT);	glVertex2f( width / 2.0f + point.getX(),	-height / 2.0f + point.getY());
		glTexCoord2f(maxS, 0.0f);	glVertex2f( width / 2.0f + point.getX(),	 height / 2.0f + point.getY());
		glTexCoord2f(0.0f, 0.0f);	glVertex2f(-width / 2.0f + point.getX(),	 height / 2.0f + point.getY());
	glEnd();
}

void GLTexture2D::drawAtPoint(const Point &point, const FLOAT32 rotation, 
                            const FLOAT32 scale)
{
   	GLfloat	width = static_cast<GLfloat>(getWidth() * maxS);
	GLfloat height = static_cast<GLfloat>(getHeight() * maxT);

    glBindTexture(GL_TEXTURE_2D, name);
	glPushMatrix();
	glTranslatef(point.getX(), point.getY(), 0);
    //in degrees, about screen origin.
	glRotatef(rotation, 0, 0, 1); 
	glScalef(scale, scale, scale);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, maxT);	glVertex2f(-width / 2.0f,	-height / 2.0f);
		glTexCoord2f(maxS, maxT);	glVertex2f( width / 2.0f,	-height / 2.0f);
		glTexCoord2f(maxS, 0.0f);	glVertex2f( width / 2.0f,	 height / 2.0f);
		glTexCoord2f(0.0f, 0.0f);	glVertex2f(-width / 2.0f,	 height / 2.0f);
	glEnd();
	glPopMatrix();
}

void GLTexture2D::drawInRect(const Rect &dest, const Rect &src, 
                           const FLOAT32 rotation, const FLOAT32 scale)
{
   	GLfloat gx0 =  src.getX() / getWidth();
    GLfloat gx1 = (src.getX() + src.getWidth()) / getWidth();
	GLfloat	gy0 =  src.getY() / getHeight();
    GLfloat gy1 = (src.getY() + src.getHeight()) / getHeight();

    glBindTexture(GL_TEXTURE_2D, name);
	glPushMatrix();
	glTranslatef(dest.getX() + dest.getWidth()/2, dest.getY() + dest.getHeight()/2, 0);
	//in degrees, about screen origin.
    glRotatef(rotation, 0, 0, 1);
    glScalef(scale, scale, 1);
	glBegin(GL_QUADS);
		glTexCoord2f(gx0, gy1);	glVertex2f(-dest.getWidth() / 2.0f, -dest.getHeight() / 2.0f);
		glTexCoord2f(gx1, gy1);	glVertex2f( dest.getWidth() / 2.0f,	-dest.getHeight() / 2.0f);
		glTexCoord2f(gx1, gy0);	glVertex2f( dest.getWidth() / 2.0f,	 dest.getHeight() / 2.0f);
		glTexCoord2f(gx0, gy0);	glVertex2f(-dest.getWidth() / 2.0f,	 dest.getHeight() / 2.0f);
	glEnd();
    glPopMatrix();
}

void GLTexture2D::drawInRect(const Rect &rect)
{
   	glBindTexture(GL_TEXTURE_2D, name);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, maxT);	glVertex2f(rect.getX() , rect.getY());
		glTexCoord2f(maxS, maxT);	glVertex2f(rect.getX() + rect.getWidth(), rect.getY());
		glTexCoord2f(maxS, 0.0f);	glVertex2f(rect.getX() + rect.getWidth(), rect.getY() + rect.getHeight());
		glTexCoord2f(0.0f, 0.0f);	glVertex2f(rect.getX() , rect.getY() + rect.getHeight());
	glEnd();
}

void GLTexture2D::drawInVertices(const GLfloat* vertices)
{
  	glBindTexture(GL_TEXTURE_2D, name);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, maxT);	glVertex2f(vertices[0], vertices[1]);
		glTexCoord2f(maxS, maxT);	glVertex2f(vertices[2], vertices[3]);
		glTexCoord2f(maxS, 0.0f);	glVertex2f(vertices[4], vertices[5]);
		glTexCoord2f(0.0f, 0.0f);	glVertex2f(vertices[6], vertices[7]);
	glEnd();
}

void GLTexture2D::initialize(void) throw(...)
{
    if (!fileName.empty())
    {
        // Open image with Corona
        corona::Image* img = corona::OpenImage(fileName.c_str());
        if (img == NULL)
        {
            string err("Error while opening file ");
            err.append(fileName);
            Logger::ERR(GAMEENGINE_LOG, err);
            throw IOException(err);
        }

        contentSize.setHeight(img->getHeight());
        contentSize.setWidth(img->getWidth());
        pixelsHigh = img->getHeight();
        pixelsWide = img->getWidth();
        data = img->getPixels();
    }

	maxS = static_cast<GLfloat>(contentSize.getWidth() / pixelsWide);
	maxT = static_cast<GLfloat>(contentSize.getHeight() / pixelsHigh);
	
	// Get a new texture id.  name increases as more textures are loaded
	glGenTextures(1, &name);

    // Generally, saveName==1.  gets existing bound texture, so we can restore 
    // it after load.
    GLint saveName;
	glGetIntegerv(GL_TEXTURE_BINDING_2D, &saveName);

    // Start working with our new texture id
	glBindTexture(GL_TEXTURE_2D, name); 

   	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	// Associate pixel data with the texture id
	switch(pixelFormat) 
    {
	case kGLTexture2DPixelFormat_RGBA8888:
		glTexImage2D(GL_TEXTURE_2D, 0, 4, pixelsWide, pixelsHigh, 0, 
            GL_RGBA, GL_UNSIGNED_BYTE, data);
		break;
	case kGLTexture2DPixelFormat_RGB565:
		glTexImage2D(GL_TEXTURE_2D, 0, 3, pixelsWide, pixelsHigh, 0, 
            GL_RGB, GL_UNSIGNED_BYTE, data);
		break;
	case kGLTexture2DPixelFormat_A8:
		glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, pixelsWide, pixelsHigh, 0, 
            GL_ALPHA, GL_UNSIGNED_BYTE, data);
		break;
	default:
		// throw exception : unknown format
        break;
	}
	
    // Restore the previous texture binding
    glBindTexture(GL_TEXTURE_2D, saveName); 
}

///////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////

const GLuint GLTexture2D::getName(void) const
{
    return name;
}

const Size GLTexture2D::getContentSize(void) const
{
    return contentSize;
}

const UINT32 GLTexture2D::getPixelsWide(void) const
{
    return pixelsWide;
}

const UINT32 GLTexture2D::getPixelsHigh(void) const
{
    return pixelsHigh;
}

const GLTexture2DPixelFormat GLTexture2D::getPixelFormat(void) const
{
    return pixelFormat;
}

const GLfloat GLTexture2D::getMaxS(void) const
{
    return maxS;
}

const GLfloat GLTexture2D::getMaxT(void) const
{
    return maxT;
}

const FLOAT32 GLTexture2D::getWidth(void) const
{
    return contentSize.getWidth();
}

const FLOAT32 GLTexture2D::getHeight(void) const
{
    return contentSize.getHeight();
}

///////////////////////////////////////////////////////////////////////////////
// Setters
///////////////////////////////////////////////////////////////////////////////

void GLTexture2D::setName(const GLuint n)
{
    name = n;
}

void GLTexture2D::setFileName(const string &file)
{
    fileName = file;
}

void GLTexture2D::setContentSize(const Size &s)
{
    contentSize = s;
}

void GLTexture2D::setPixelsWide(const UINT32 pw)
{
    pixelsWide = pw;
}

void GLTexture2D::setPixelsHigh(const UINT32 ph)
{
    pixelsHigh = ph;
}

void GLTexture2D::setPixelFormat(const GLTexture2DPixelFormat pf)
{
    pixelFormat = pf;
}

void GLTexture2D::setMaxS(const GLfloat m)
{
    maxS = m;
}

void GLTexture2D::setMaxT(const GLfloat m)  
{
    maxT = m;
}
